package com.zksite.nator.processor.http;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.zksite.nator.common.config.Application;
import com.zksite.nator.common.config.Config;
import com.zksite.nator.common.constants.Constants;
import com.zksite.nator.common.constants.ErrorCode;
import com.zksite.nator.common.model.ResponseModel;
import com.zksite.nator.common.processor.RequestProcessor;
import com.zksite.nator.common.util.Reflections;
import com.zksite.nator.processor.http.container.DefaultClientContainer;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;

@Component("internalRequestProcessor")
public class InternalRequestProcessor implements RequestProcessor {

    private static final Logger LOGGER = LoggerFactory.getLogger(InternalRequestProcessor.class);

    @Resource(name = "defaultClientContainer")
    private DefaultClientContainer clientContainer;

    @Override
    public boolean process(ChannelHandlerContext ctx, Object msg) {
        HttpResponse response = (HttpResponse) msg;
        String messageType = response.headers().get(Constants.MESSAGE_TYPE);
        Method method = Reflections.getAccessibleMethodByName(this, messageType);
        if (method == null) {
            return false;
        }
        try {
            method.invoke(this, ctx, msg);
            return true;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean responseMapping(ChannelHandlerContext ctx, Object msg) {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        LOGGER.info("receive mapping message.host:{}", insocket.getAddress());
        FullHttpResponse response = (FullHttpResponse) msg;
        try {
            byte[] body = new byte[response.content().readableBytes()];
            response.content().getBytes(0, body);
            String json = new String(body, "utf-8");
            LOGGER.info("start mapping.{}", json);
            Config config = JSON.parseObject(json, Config.class);
            for (Application application : config.getApplications()) {
                try {
                    clientContainer.put(application.getDomain(), ctx.channel());
                } catch (IllegalStateException e) {
                    URI uri;
                    try {
                        uri = new URI("http://127.0.0.1");
                        ResponseModel responseModel = new ResponseModel(
                                ErrorCode.BIZ_EXCEPTION.getErrcode(), e.getMessage(), null);
                        byte[] content = JSON.toJSONString(responseModel).getBytes();
                        HttpRequest request =
                                new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
                                        uri.toASCIIString(), Unpooled.wrappedBuffer(content));
                        request.headers().add(Constants.MESSAGE_TYPE, Constants.MESSAGE_INFO);
                        request.headers().add("Content-Length", content.length);
                        ctx.writeAndFlush(request);
                        LOGGER.error("ADD MAPPING ERROR.{}", e);
                        return false;
                    } catch (URISyntaxException e1) {
                        e1.printStackTrace();
                    }
                }
            }

        } catch (UnsupportedEncodingException e) {

        } finally {
            ReferenceCountUtil.release(msg);
        }
        return true;
    }

    public boolean pong(ChannelHandlerContext ctx, Object msg) {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        LOGGER.debug("receive pong message.host:{}", insocket.getAddress());
        ReferenceCountUtil.release(msg);
        return true;
    }
}
